package com.linktco.common.aop;

import com.linktco.common.utils.U;
import lombok.extern.slf4j.Slf4j;
import org.springblade.core.tool.utils.Base64Util;
import org.springframework.stereotype.Component;
import org.springframework.util.Assert;
import org.springframework.web.servlet.HandlerInterceptor;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.time.Instant;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.time.temporal.ChronoUnit;
import java.util.*;
import java.util.regex.Pattern;

/**
 * @author zhangnx
 */
@Component
@Slf4j
public class GlobalControllerInterceptor implements HandlerInterceptor {

    private final static String SQL_REGEX = "(?i)(?<![a-z])('|%|--|1=1|or|insert|delete|select|sleep|count|updatexml|group|union|1=1|drop|truncate|alter|grant|execute|exec|xp_cmdshell|call|declare|sql)(?![a-z])";
    private final static Pattern PATTERN = Pattern.compile("(?:--|\\b1=1\\b|\\bor\\b|\\binsert\\b|\\bdelete\\b|\\bselect\\b|\\bcount\\b|\\bupdatexml\\b|\\bsleep\\b|group\\s+by|\\bunion\\b|\\bdrop\\b|\\btruncate\\b|\\balter\\b|\\bgrant\\b|\\bexecute\\b|\\bxp_cmdshell\\b|\\bcall\\b|\\bdeclare\\b|\\bsql\\b)");

    private List<String> excludePaths = new ArrayList<String>(){{
        add("/blade-auth/oauth/token");
        add("/blade-flow/process/diagram-view");
        add("/doc.html");
        add("/swagger-resources");
        add("/webjars");
    }};


    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) {
        String requestURI = request.getRequestURI();
        Optional<String> first = excludePaths.stream().filter(path -> requestURI.contains(path)).findFirst();
        if (first.isPresent()){
            return true;
        }

        String verifyCode = request.getHeader("Verify-Code");
        try {
            verifyCode = U.reverse(verifyCode);
            String decode = Base64Util.decode(verifyCode);
            String reverse = U.reverse(decode);

            Instant instant = Instant.ofEpochMilli(Long.valueOf(reverse));
            LocalDateTime localDate = LocalDateTime.ofInstant(instant, ZoneId.systemDefault());

            long seconds = ChronoUnit.SECONDS.between(localDate, LocalDateTime.now());
            log.info("请求校验两个时间相差：{} 秒",Math.abs(seconds) );
            Assert.isTrue( Math.abs(seconds) <= 10, "非法请求!");
        } catch (Exception e) {
            log.error(e.getMessage());
            throw new IllegalArgumentException("非法请求！");
        }


        List<String> list = new ArrayList<>();
        Enumeration<String> names = request.getParameterNames();
        while (names.hasMoreElements()) {
            String name = names.nextElement();
            list.add(name);
            String[] values = request.getParameterValues(name);
            list.addAll(Arrays.asList(values));
        }
        if (paramsValidate(list)) {
            throw new IllegalArgumentException("您请求的参数中有非法字符");
        } else {
            return true;
        }
    }




    private boolean paramsValidate(List<String> list) {
        for (String s : list) {
            s = s.trim().toLowerCase();
            if (s.contains("[]")){
                continue;
            }
            boolean matches = SQL_REGEX.matches(s);
            boolean matcher = PATTERN.matcher(s).find();
            if (matches || matcher){
                return true;
            }
        }
        return false;
    }


}